/**
 * @license
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
import '../../../styles/gr-form-styles.js';
import '../../../styles/gr-subpage-styles.js';
import '../../../styles/shared-styles.js';
import '../../shared/gr-autocomplete/gr-autocomplete.js';
import '../../shared/gr-copy-clipboard/gr-copy-clipboard.js';
import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
import '../../shared/gr-select/gr-select.js';
import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import {htmlTemplate} from './gr-group_html.js';

const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;

const OPTIONS = {
  submitFalse: {
    value: false,
    label: 'False',
  },
  submitTrue: {
    value: true,
    label: 'True',
  },
};

/**
 * @extends PolymerElement
 */
class GrGroup extends GestureEventListeners(
    LegacyElementMixin(PolymerElement)) {
  static get template() { return htmlTemplate; }

  static get is() { return 'gr-group'; }
  /**
   * Fired when the group name changes.
   *
   * @event name-changed
   */

  static get properties() {
    return {
      groupId: Number,
      _rename: {
        type: Boolean,
        value: false,
      },
      _groupIsInternal: Boolean,
      _description: {
        type: Boolean,
        value: false,
      },
      _owner: {
        type: Boolean,
        value: false,
      },
      _options: {
        type: Boolean,
        value: false,
      },
      _loading: {
        type: Boolean,
        value: true,
      },
      /** @type {?} */
      _groupConfig: Object,
      _groupConfigOwner: String,
      _groupName: Object,
      _groupOwner: {
        type: Boolean,
        value: false,
      },
      _submitTypes: {
        type: Array,
        value() {
          return Object.values(OPTIONS);
        },
      },
      _query: {
        type: Function,
        value() {
          return this._getGroupSuggestions.bind(this);
        },
      },
      _isAdmin: {
        type: Boolean,
        value: false,
      },
    };
  }

  static get observers() {
    return [
      '_handleConfigName(_groupConfig.name)',
      '_handleConfigOwner(_groupConfig.owner, _groupConfigOwner)',
      '_handleConfigDescription(_groupConfig.description)',
      '_handleConfigOptions(_groupConfig.options.visible_to_all)',
    ];
  }

  /** @override */
  attached() {
    super.attached();
    this._loadGroup();
  }

  _loadGroup() {
    if (!this.groupId) { return; }

    const promises = [];

    const errFn = response => {
      this.dispatchEvent(new CustomEvent('page-error', {
        detail: {response},
        composed: true, bubbles: true,
      }));
    };

    return this.$.restAPI.getGroupConfig(this.groupId, errFn)
        .then(config => {
          if (!config || !config.name) { return Promise.resolve(); }

          this._groupName = config.name;
          this._groupIsInternal = !!config.id.match(INTERNAL_GROUP_REGEX);

          promises.push(this.$.restAPI.getIsAdmin().then(isAdmin => {
            this._isAdmin = !!isAdmin;
          }));

          promises.push(this.$.restAPI.getIsGroupOwner(config.name)
              .then(isOwner => {
                this._groupOwner = !!isOwner;
              }));

          // If visible to all is undefined, set to false. If it is defined
          // as false, setting to false is fine. If any optional values
          // are added with a default of true, then this would need to be an
          // undefined check and not a truthy/falsy check.
          if (!config.options.visible_to_all) {
            config.options.visible_to_all = false;
          }
          this._groupConfig = config;

          this.dispatchEvent(new CustomEvent('title-change', {
            detail: {title: config.name},
            composed: true, bubbles: true,
          }));

          return Promise.all(promises).then(() => {
            this._loading = false;
          });
        });
  }

  _computeLoadingClass(loading) {
    return loading ? 'loading' : '';
  }

  _isLoading() {
    return this._loading || this._loading === undefined;
  }

  _handleSaveName() {
    return this.$.restAPI.saveGroupName(this.groupId, this._groupConfig.name)
        .then(config => {
          if (config.status === 200) {
            this._groupName = this._groupConfig.name;
            this.dispatchEvent(new CustomEvent('name-changed', {
              detail: {name: this._groupConfig.name,
                external: this._groupIsExtenral},
              composed: true, bubbles: true,
            }));
            this._rename = false;
          }
        });
  }

  _handleSaveOwner() {
    let owner = this._groupConfig.owner;
    if (this._groupConfigOwner) {
      owner = decodeURIComponent(this._groupConfigOwner);
    }
    return this.$.restAPI.saveGroupOwner(this.groupId,
        owner).then(config => {
      this._owner = false;
    });
  }

  _handleSaveDescription() {
    return this.$.restAPI.saveGroupDescription(this.groupId,
        this._groupConfig.description).then(config => {
      this._description = false;
    });
  }

  _handleSaveOptions() {
    const visible = this._groupConfig.options.visible_to_all;

    const options = {visible_to_all: visible};

    return this.$.restAPI.saveGroupOptions(this.groupId,
        options).then(config => {
      this._options = false;
    });
  }

  _handleConfigName() {
    if (this._isLoading()) { return; }
    this._rename = true;
  }

  _handleConfigOwner() {
    if (this._isLoading()) { return; }
    this._owner = true;
  }

  _handleConfigDescription() {
    if (this._isLoading()) { return; }
    this._description = true;
  }

  _handleConfigOptions() {
    if (this._isLoading()) { return; }
    this._options = true;
  }

  _computeHeaderClass(configChanged) {
    return configChanged ? 'edited' : '';
  }

  _getGroupSuggestions(input) {
    return this.$.restAPI.getSuggestedGroups(input)
        .then(response => {
          const groups = [];
          for (const key in response) {
            if (!response.hasOwnProperty(key)) { continue; }
            groups.push({
              name: key,
              value: decodeURIComponent(response[key].id),
            });
          }
          return groups;
        });
  }

  _computeGroupDisabled(owner, admin, groupIsInternal) {
    return !(groupIsInternal && (admin || owner));
  }

  _getGroupUUID(id) {
    if (!id) return;

    return id.match(INTERNAL_GROUP_REGEX) ? id : decodeURIComponent(id);
  }
}

customElements.define(GrGroup.is, GrGroup);
